package com.cn.lp.jpa.cq.test.domain;

import com.google.common.collect.Maps;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 计划执行器
 */
//@Component
public class ScheduleExecutor {

    private ExecutorService runThreadPool;

    private ExecutorService heartbeatThreadPool;

    private Map<String, ScheduleTask> names2Tasks = Maps.newHashMap();

    private ConcurrentLinkedDeque<ScheduleTask> tasks = new ConcurrentLinkedDeque<>();

    private ConcurrentLinkedDeque<ScheduleRunTask> runTasks = new ConcurrentLinkedDeque<>();

    private int runTaskCacheNum = 50;

    private volatile boolean running;

    public ScheduleExecutor() {
        this(Executors.newCachedThreadPool());
    }

    public ScheduleExecutor(int runThreadNum, int runTaskCacheNum) {
        this(Executors.newFixedThreadPool(runThreadNum));
        this.runTaskCacheNum = runTaskCacheNum;
    }

    public ScheduleExecutor(ExecutorService runThreadPool) {
        this.runThreadPool = runThreadPool;
        this.heartbeatThreadPool = Executors.newSingleThreadExecutor();
        this.running = true;
        this.heartbeatThreadPool.submit(() -> {
            while (true) {
                if (running) {
                    try {
                        for (ScheduleTask task : tasks) {
                            if (task.canExec() && task.mark()) {
                                ScheduleRunTask runTask = runTasks.poll();
                                if (runTask == null) {
                                    runTask = new ScheduleRunTask();
                                }
                                runTask.initTask(task);
                                runThreadPool.submit(runTask);
                            }
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                Thread.sleep(250);
            }
        });
    }

    public ScheduleTask getTask(String name) {
        return names2Tasks.get(name);
    }

    public void stop() {
        running = false;
    }

    public void restart() {
        running = true;
    }

    public void release() {
        this.stop();
        heartbeatThreadPool.shutdown();
        runThreadPool.shutdown();
    }

    public synchronized void addTask(ScheduleTask task) {
        if (!names2Tasks.containsKey(task.getName())) {
            names2Tasks.putIfAbsent(task.getName(), task);
            tasks.add(task);
        }
    }

    public synchronized void removeTask(String name) {
        ScheduleTask task = names2Tasks.get(name);
        if (Objects.nonNull(task)) {
            names2Tasks.remove(name);
            tasks.remove(task);
        }
    }

    public class ScheduleRunTask implements Runnable {

        private ScheduleTask task;

        public void initTask(ScheduleTask task) {
            this.task = task;
        }

        public void clear() {
            this.task = null;
        }

        @Override
        public void run() {
            try {
                task.exec();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                task.cancelMark();
                this.clear();
                if (runTasks.size() <= runTaskCacheNum) {
                    runTasks.add(this);
                }
            }
        }

    }

}
